/*------------------------------------------------------------------------------
This file is part of MUTS.

    MUTS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MUTS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------*/

#include "stdafx.h"
#include "Win32WebServer.h"

struct {
	char *ext;
	char *filetype;
} g_arrExtensions [] = {
	{"gif", "image/gif" },  
	{"jpg", "image/jpeg"}, 
	{"jpeg","image/jpeg"},
	{"png", "image/png" },
	{"zip", "image/zip" },
	{"gz",  "image/gz"  },
	{"tar", "image/tar" },
	{"htm", "text/html" },
	{"html","text/html" },
	{"xml", "text/xml" },
	{"js",  "text/javascript" },
	{"css", "text/css" },
	{"flv", "video/x-flv" },
	{"ogg", "video/ogg" },
	{"asf", "video/x-ms-asf" },
	{"swf", "application/x-shockwave-flash" },
	{"cab", "application/x-shockwave-flash" },
	{0,0} };

//Global hit counter
long g_lngHit = 0;

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : this is a child web server process, so we can exit on errors
// Parameters :
//		l_pdwThreadParam - File Handler to the web client's response (HTTP output)
//							(pointer to int)
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
DWORD win32_web(LPVOID p_pdwThreadParam)
{
	SOCKET l_typClientSocket =  * ((SOCKET *) p_pdwThreadParam);
	char l_arrRecvBuffer[DEFAULT_BUFLEN];
	char l_arrSendBuffer[RETURN_BUFLEN];
	int i, j;
	int l_intResult;
	int l_intSendResult;
	int l_intRecvBufLen = DEFAULT_BUFLEN;
	char * l_ptrFileType;
	int l_intLength;	//HTTP Request Length
	int l_intLocalFileHandler;

	// Receive until the peer shuts down the connection
	do {
			l_intResult = recv(l_typClientSocket, l_arrRecvBuffer, l_intRecvBufLen, 0);
			if (l_intResult > 0) {
			printf("Bytes received: %d\n", l_intResult);

			if(l_intResult > 0 && l_intResult < DEFAULT_BUFLEN)	/* return code is valid chars */
				l_arrRecvBuffer[l_intResult] = 0;		/* terminate the buffer */
			printf("Buffer content: %s\n", l_arrRecvBuffer);

			if (strncmp(l_arrRecvBuffer,"GET ",4) && strncmp(l_arrRecvBuffer,"get ",4) )
				printf("Only simple GET operation supported: %s\n", l_arrRecvBuffer);

			for (i=4; i<DEFAULT_BUFLEN; i++) { /* null terminate after the second space to ignore extra stuff */
				if (l_arrRecvBuffer[i] == ' ') { /* string is "GET URL " +lots of other stuff */
					l_arrRecvBuffer[i] = 0;
					break;
				}
			}

			for (j=0; j<i-1; j++) 	/* check for illegal parent directory use .. */
				if(l_arrRecvBuffer[j] == '.' && l_arrRecvBuffer[j+1] == '.')
					printf("Parent directory (..) path names not supported: %s\n", l_arrRecvBuffer);

			if( !strncmp(&l_arrRecvBuffer[0],"GET /\0",6) || !strncmp(&l_arrRecvBuffer[0],"get /\0",6) ) /* convert no filename to index file */
				(void)strcpy_s(l_arrRecvBuffer,"GET /index.html");

			/* work out the file type and check we support it */
			l_intRecvBufLen = strlen(l_arrRecvBuffer);
			l_ptrFileType = (char *) 0;
			for(i=0; g_arrExtensions[i].ext != 0; i++) {
				l_intLength = strlen(g_arrExtensions[i].ext);
				if( !strncmp(&l_arrRecvBuffer[l_intRecvBufLen - l_intLength], g_arrExtensions[i].ext, l_intLength)) {
					l_ptrFileType = g_arrExtensions[i].filetype;
					break;
				}
			}
			if(l_ptrFileType == 0){
				printf("file extension type not supported: %s\n", l_arrRecvBuffer);
				l_ptrFileType = g_arrExtensions[13].filetype;
				//break;
			}

			if (!strcmp(l_ptrFileType, "video/x-flv")){
				//Stremaing required
				wchar_t l_strParams[1024];
				(void)swprintf_s(l_strParams, L"ffmpeg.exe -i sample.mpg -vcodec flv -ab 36k -ar 48000 -acodec libmp3lame -f flv -");

				(void)sprintf_s(l_arrSendBuffer,"HTTP/1.0 200 OK\r\n"
												"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
												"Cache-Control: no-store, no-cache, must-revalidate\r\n"
												"Cache-Control: post-check=0, pre-check=0\r\n"
												"Pragma: no-cache\r\n"
												/*"Content-Length: 992347456\r\n"*/
												"Content-Type: %s\r\n\r\n", l_ptrFileType);
				l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, strlen(l_arrSendBuffer), 0);
				win32_stream(l_strParams, &l_typClientSocket);
			}
			else if (!strcmp(l_ptrFileType, "video/ogg")){
				wchar_t l_strParams[1024];
				(void)swprintf_s(l_strParams, L"ffmpeg.exe -i sample.mpg -vcodec libtheora -sameq -acodec libvorbis -f ogg -");

				(void)sprintf_s(l_arrSendBuffer,"HTTP/1.0 200 OK\r\n"
												"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
												"Cache-Control: no-store, no-cache, must-revalidate\r\n"
												"Cache-Control: post-check=0, pre-check=0\r\n"
												"Pragma: no-cache\r\n"
												/*"Content-Length: 992347456\r\n"*/
												"Content-Type: %s\r\n\r\n", l_ptrFileType);
				l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, strlen(l_arrSendBuffer), 0);
				win32_stream(l_strParams, &l_typClientSocket);
			}
			else if (!strcmp(l_ptrFileType, "video/x-ms-asf")){
				wchar_t l_strParams[1024];
				(void)swprintf_s(l_strParams, L"ffmpeg.exe -i sample.mpg -vcodec wmv2 -acodec libmp3lame -f asf -");

				(void)sprintf_s(l_arrSendBuffer,"HTTP/1.0 200 OK\r\n"
												"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
												"Cache-Control: no-store, no-cache, must-revalidate\r\n"
												"Cache-Control: post-check=0, pre-check=0\r\n"
												"Pragma: no-cache\r\n"
												/*"Content-Length: 992347456\r\n"*/
												"Content-Type: %s\r\n\r\n", l_ptrFileType);
				l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, strlen(l_arrSendBuffer), 0);
				win32_stream(l_strParams, &l_typClientSocket);
			}
			else{

				(void)_sopen_s(&l_intLocalFileHandler, &l_arrRecvBuffer[5], _O_BINARY | _O_RDONLY, _SH_DENYNO, _S_IREAD | _S_IWRITE);
				if(l_intLocalFileHandler <= 0){
					printf("failed to open file: %s\n",  &l_arrRecvBuffer[5]);
					(void)sprintf_s(l_arrSendBuffer,"HTTP/1.0 404 Bad request\r\nContent-Type: text/html\r\n\r\n"
						"<html>\n<head>\n\t<title>BadRequest</title>\n\t</head>\n"
						"<body>\nError : Bad Request</body>\n</html>\n", l_ptrFileType);
					l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, strlen(l_arrSendBuffer), 0);
					break;
				}

				printf("SEND (%i) : %s\n", g_lngHit, &l_arrRecvBuffer[5]);

				(void)sprintf_s(l_arrSendBuffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", l_ptrFileType);
				l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, strlen(l_arrSendBuffer), 0);

				/* send file in 8KB block - last block may be smaller */
				while (	(l_intResult = _read(l_intLocalFileHandler, l_arrSendBuffer, RETURN_BUFLEN)) > 0 ) {
					l_intSendResult = send(l_typClientSocket, l_arrSendBuffer, l_intResult, 0);
					if (l_intSendResult == SOCKET_ERROR) {
						printf("send failed: %d\n", WSAGetLastError());
						closesocket(l_typClientSocket);
						//WSACleanup();
						return 1;
					}
				}
				(void)_close(l_intLocalFileHandler);
			}
		}
	} while (l_intResult > 0);

	// shutdown the send half of the connection since no more data will be sent
	l_intResult = shutdown(l_typClientSocket, SD_SEND);
	if (l_intResult == SOCKET_ERROR) {
		printf("shutdown failed: %d\n", WSAGetLastError());
		closesocket(l_typClientSocket);
		//WSACleanup();
		return 1;
	}

	//End of thread
	closesocket(l_typClientSocket);
	printf("End of thread\n");
	return 0;
}

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : accept connection and create threads that will handle each request.
//				  
// Parameters :
//		p_strPort - Port number to be listened
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
int win32_WaitForWebRequest(const char * p_strPort)
{
	DWORD l_dwThreadId;
	SOCKET l_typClientSocket = INVALID_SOCKET;
	SOCKET l_typListenSocket = INVALID_SOCKET;

	l_typListenSocket = init_socket();

	for(g_lngHit=1; ;g_lngHit++) {


		// Accept a client socket
		l_typClientSocket = accept(l_typListenSocket, NULL, NULL);
		if (l_typClientSocket == INVALID_SOCKET) {
			printf("accept failed: %d\n", WSAGetLastError());
			closesocket(l_typListenSocket);
			l_typListenSocket = init_socket();
			if (INVALID_SOCKET == l_typListenSocket){
				printf("2/2 accept failed: %d\n", WSAGetLastError());
				return 1;
			}
		}  
		printf("system call / accept\n");
		if (CreateThread(NULL, //Choose default security
				0, //Default stack size
				(LPTHREAD_START_ROUTINE)&win32_web,
				//Routine to execute
				(LPVOID) &l_typClientSocket, //Thread parameter
				0, //Immediately run the thread
				&l_dwThreadId //Thread Id
				) == NULL){
			printf("Thread creation failed\n");
		}
	}
}

//--------------------------------------------------------------------------------
// Author : Benjamin BALET
// Date : 2010-04-03
// Description : Initialize a windows socket (Listen)
//
// Parameters :
//		p_strPort - Port number to be listened
// Return : Result of the thread
// Changes : yyyy-mm-dd - description of change
//--------------------------------------------------------------------------------
SOCKET init_socket(void)
{
	WSADATA wsaData;
	int iResult;
	SOCKET ListenSocket = INVALID_SOCKET;

	// Initialize Winsock
	WSACleanup();
	iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return INVALID_SOCKET;
	}
	struct addrinfo *result = NULL, *ptr = NULL, hints;

	ZeroMemory(&hints, sizeof (hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;

	// Resolve the local address and port to be used by the server
	iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
	if (iResult != 0) {
		printf("getaddrinfo failed: %d\n", iResult);
		WSACleanup();
		return INVALID_SOCKET;
	}

	// Create a SOCKET for the server to listen for client connections
	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (ListenSocket == INVALID_SOCKET) {
		printf("Error at socket(): %ld\n", WSAGetLastError());
		freeaddrinfo(result);
		WSACleanup();
		return INVALID_SOCKET;
	}

	// Setup the TCP listening socket
	iResult = bind( ListenSocket, 
		result->ai_addr, (int)result->ai_addrlen);
	if (iResult == SOCKET_ERROR) {
		printf("bind failed: %d\n", WSAGetLastError());
		freeaddrinfo(result);
		closesocket(ListenSocket);
		WSACleanup();
		return INVALID_SOCKET;
	}

	if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
		printf( "Error at bind(): %ld\n", WSAGetLastError() );
		closesocket(ListenSocket);
		WSACleanup();
		return INVALID_SOCKET;
	}

	return ListenSocket;
}